const fs = require('fs-extra');
const mongoose = require('mongoose');
const path = require('path');
const { getDefects } = require('../../../defects');
const { getLinesOfContentBatch } = require('../../../files');
const DefaultReporter = require('../static-analyze-report');
const Func = require('../../../mongo/function');

const levelKeyCn = {
  1: '强制',
  2: '要求/必要/强烈推荐',
  3: '建议/推荐',
};

class StaticAnalyzeReporter extends DefaultReporter {
  async generate() {
    // if (this.format !== 'csv') {
    //   throw new Error('only support csv format');
    // }
    const funcs = (await Func.find({
      versionId: mongoose.Types.ObjectId(this.versionId),
    }, ['fileId', 'functionName', 'functionFullName', 'location', 'endLocation', 'kind']).lean()).filter((func) => func.location && func.endLocation);
    const funcMap = funcs.reduce((obj, item) => {
      const key = `${item.fileId}_${item.location.line}_${item.endLocation.line}`;
      obj[key] = item;
      return obj;
    }, {});
    let defects = await getDefects(this.versionId, {
      ruleSets: this.filters.ruleSets,
      levels: this.filters.levels,
      status: this.filters.defectStatus,
    });
    defects = defects.sort((a, b) => {
      if (a.filePath !== b.filePath) {
        return a.filePath > b.filePath ? 1 : -1;
      }
      if (a.ruleSetName !== b.ruleSetName) {
        return a.ruleSetName > b.ruleSetName ? 1 : -1;
      }
      if (a.ruleCode !== b.ruleCode) {
        return a.ruleCode > b.ruleCode ? 1 : -1;
      }
      return 0;
    }).map((val) => ({
      fileId: val.fileId,
      filePath: val.filePath,
      level: levelKeyCn[val.level] || val.level,
      ruleDesc: `[${val.ruleSetName}]${val.ruleCode} ${val.ruleName}`,
      locations: val.locations,
    }));

    // note: 以下逻辑必须保证defects按fileId排序
    let startIdx = 0;
    let lineRanges = [];
    for (let idx = 0; idx < defects.length; idx += 1) {
      const defect = defects[idx];
      const nextDefect = defects[idx + 1];
      lineRanges.push([defect.locations.begin.line, defect.locations.end.line]);
      if (!nextDefect || defect.fileId.toString() !== nextDefect.fileId.toString()) {
        // eslint-disable-next-line no-await-in-loop
        const arr = await getLinesOfContentBatch(defect.fileId, lineRanges);
        for (let index = startIdx; index <= idx; index += 1) {
          // 如果缺陷代码行和函数代码行完全匹配，则只展示文件名即可。避免显示行数过多
          const funcKey = `${defects[index].fileId}_${defects[index].locations.begin.line}_${defects[index].locations.end.line}`;
          const func = funcMap[funcKey];
          if (func) {
            defects[index].lines = [func.functionName];
          } else {
            defects[index].lines = arr[index - startIdx];
          }
        }
        startIdx = idx + 1;
        lineRanges = [];
      }
    }
    const result = {
      cachePath: this.outputDir,
    };
    const fileName = `${this.project.projectName}-${this.version.versionName}-${this.getReportName()}.csv`;
    result.reportPath = path.join(this.outputDir, fileName);
    await fs.ensureFile(result.reportPath);
    const ws = fs.createWriteStream(result.reportPath);
    ws.write('\ufeff'); // add BOM header, 兼容excel
    ws.write('"问题编号","所在文件","严重度","行列号","附加说明","规则说明","开发确认","备注","回归确认"');
    // eslint-disable-next-line no-restricted-syntax
    for (const defect of defects) {
      defect.lines = defect.lines || []; // 修复报错
      ws.write(`
"",\
"${defect.filePath}",\
"${defect.level}",\
"(行${defect.locations.begin.line},列${defect.locations.begin.column})",\
"${defect.lines.join('\n').replace(/"/g, '""')}",\
"${defect.ruleDesc.replace(/"/g, '""')}",\
"",\
"",\
""`);
    }
    ws.end('');
    await new Promise((res, rej) => {
      ws.once('finish', res);
      ws.once('error', rej);
    });
    return result;
  }
}

module.exports = StaticAnalyzeReporter;
